﻿namespace wToggl
{
    using System;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    using System.Threading.Tasks;

    using Windows.UI.Core;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Navigation;

    using wToggl.Common;

    using wToggle.Platform;
    using wToggle.Platform.Annotations;
    using wToggle.Platform.Data;
    using wToggle.Platform.Interfaces;

    using XPlatUtils;

    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class ClientPage : INotifyPropertyChanged
    {
        private NavigationHelper navigationHelper;
        private ObservableDictionary defaultViewModel = new ObservableDictionary();

        private Client client;

        private string clientName;

        private bool isWorking;

        private string overlayMessage = "Loading data...";

        public ClientPage()
        {
            this.InitializeComponent();

            this.navigationHelper = new NavigationHelper(this);
            this.navigationHelper.LoadState += this.NavigationHelper_LoadState;
            this.navigationHelper.SaveState += this.NavigationHelper_SaveState;
        }

        /// <summary>
        /// Gets the <see cref="NavigationHelper"/> associated with this <see cref="Page"/>.
        /// </summary>
        public NavigationHelper NavigationHelper
        {
            get { return this.navigationHelper; }
        }

        /// <summary>
        /// Gets the view model for this <see cref="Page"/>.
        /// This can be changed to a strongly typed view model.
        /// </summary>
        public ObservableDictionary DefaultViewModel
        {
            get { return this.defaultViewModel; }
        }

        public Client Client
        {
            get
            {
                return this.client;
            }

            set 
            {
                this.client = value;
                this.OnPropertyChanged();
            }
        }

        public string ClientName
        {
            get
            {
                return this.clientName;
            }
            set
            {
                this.clientName = value;
                this.OnPropertyChanged();
            }
        }

        /// <summary>
        /// Populates the page with content passed during navigation.  Any saved state is also
        /// provided when recreating a page from a prior session.
        /// </summary>
        /// <param name="sender">
        /// The source of the event; typically <see cref="NavigationHelper"/>
        /// </param>
        /// <param name="e">Event data that provides both the navigation parameter passed to
        /// <see cref="Frame.Navigate(Type, Object)"/> when this page was initially requested and
        /// a dictionary of state preserved by this page during an earlier
        /// session.  The state will be null the first time a page is visited.</param>
        private async void NavigationHelper_LoadState(object sender, LoadStateEventArgs e)
        {
            if (e.NavigationParameter == null)
            {
                throw new InvalidOperationException("Empty navigation parameter passed to ClientPage");
            }

            if (!(e.NavigationParameter is string))
            {
                throw new ArgumentException("Navigation argument passed to ClientPage has incorrect type");
            }

            try
            {
                this.IsWorking = true;
                this.OverlayMessage = "Loading client...";
                var settings = ServiceContainer.Resolve<IApplicationSettings>();
                var auth = ServiceContainer.Resolve<ITogglAuth>();

                if (!settings.HasLogin() || await auth.Authenticate(settings.Username, settings.Password) != AuthStatus.Success)
                {
                    await this.Dispatcher.RunAsync(
                        CoreDispatcherPriority.Normal,
                        () =>
                            {
                                this.Frame.Navigate(typeof(LoginPage));
                            });

                    return;
                }

                var parameter = (string)e.NavigationParameter;

                if (parameter.StartsWith("c"))
                {
                    await this.LoadClient(ulong.Parse(parameter.Substring(1)));
                }
                else if (parameter.StartsWith("w"))
                {
                    this.workspaceId = ulong.Parse(parameter.Substring(1));
                }
            
                if (e.PageState == null)
                {
                    return;
                }

                if (e.PageState.ContainsKey("name"))
                {
                    this.clientName = e.PageState["name"] as string;
                    await this.OnPropertyChangedAsync("ClientName");
                }
            }
            finally
            {
                this.IsWorking = false;
            }
        }

        private ulong workspaceId;

        public bool IsWorking
        {
            get
            {
                return this.isWorking;
            }

            set
            {
                this.isWorking = value;
                this.OnPropertyChanged();
            }
        }

        public string OverlayMessage
        {
            get
            {
                return this.overlayMessage;
            }
            set
            {
                this.overlayMessage = value;
                this.OnPropertyChanged();
            }
        }

        private async Task LoadClient(ulong id, bool silent = false)
        {
            var provider = ServiceContainer.Resolve<IClientProvider>();

            this.Client = await provider.GetClient(id);

            if (this.Client != null)
            {
                this.workspaceId = this.Client.WorkspaceId;
                this.clientName = this.Client.Name;
                await this.OnPropertyChangedAsync("ClientName");
            }
            else
            {
                if (!silent)
                {
                    await this.Dispatcher.ShowMessage(
                        "Client could not be loaded. Verify your internet connectivity and try again.",
                        "Toggly");
                }

                this.ClientName = null;
            }
        }

        /// <summary>
        /// Preserves state associated with this page in case the application is suspended or the
        /// page is discarded from the navigation cache.  Values must conform to the serialization
        /// requirements of <see cref="SuspensionManager.SessionState"/>.
        /// </summary>
        /// <param name="sender">The source of the event; typically <see cref="NavigationHelper"/></param>
        /// <param name="e">Event data that provides an empty dictionary to be populated with
        /// serializable state.</param>
        private void NavigationHelper_SaveState(object sender, SaveStateEventArgs e)
        {
            e.PageState["name"] = this.ClientName;          
        }

        #region NavigationHelper registration

        /// <summary>
        /// The methods provided in this section are simply used to allow
        /// NavigationHelper to respond to the page's navigation methods.
        /// <para>
        /// Page specific logic should be placed in event handlers for the  
        /// <see cref="NavigationHelper.LoadState"/>
        /// and <see cref="NavigationHelper.SaveState"/>.
        /// The navigation parameter is available in the LoadState method 
        /// in addition to page state preserved during an earlier session.
        /// </para>
        /// </summary>
        /// <param name="e">Provides data for navigation methods and event
        /// handlers that cannot cancel the navigation request.</param>
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            Analytics.ReportPageVisit(this);  
            this.navigationHelper.OnNavigatedTo(e);
        }

        protected override void OnNavigatedFrom(NavigationEventArgs e)
        {
            this.navigationHelper.OnNavigatedFrom(e);
        }

        #endregion

        public event PropertyChangedEventHandler PropertyChanged;

        [NotifyPropertyChangedInvocator]
        private void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            this.Dispatcher.RunAsync(
                CoreDispatcherPriority.Normal,
                () =>
                {
                    var handler = this.PropertyChanged;
                    if (handler != null)
                    {
                        handler(this, new PropertyChangedEventArgs(propertyName));
                    }
                });
        }

        private async Task OnPropertyChangedAsync(string propertyName = null)
        {
            await this.Dispatcher.RunAsync(
                CoreDispatcherPriority.Normal,
                () =>
                {
                    var handler = this.PropertyChanged;
                    if (handler != null)
                    {
                        handler(this, new PropertyChangedEventArgs(propertyName));
                    }
                });
        }

        private void BackButton_OnClick(object sender, RoutedEventArgs e)
        {
            this.Frame.GoBack();
        }

        private async void SaveItemButton_OnClick(object sender, RoutedEventArgs e)
        {
            if (string.IsNullOrEmpty(this.ClientName))
            {
                this.Dispatcher.ShowMessage("Client name must not be empty.", "Toggly");
                return;
            }

            try
            {
                this.IsWorking = true;
                this.OverlayMessage = "Saving...";
                var provider = ServiceContainer.Resolve<IClientProvider>();

                if (this.Client == null)
                {
                    var c = new Client { Name = this.ClientName, WorkspaceId = this.workspaceId };
                    await provider.Create(c);
                }
                else
                {
                    this.Client.Name = this.ClientName;
                    await provider.Update(this.client);
                }
            }
            finally
            {
                this.IsWorking = false;
            }
            
            this.Frame.GoBack();
        }
    }
}
